﻿using Pluralize.NET;
using System;
using System.Collections.Generic;
using System.Linq;
using Yuchen.NPA.Core;

namespace Yuchen.NPA.ExtensionUtility
{
    public class CompletionGenerator
    {
        private List<string> methods;
        private IEnumerable<string> methodNamePrefixs;
        private List<string> propertyMethods;
        private List<string> allMethodPrefixs;
        public IEnumerable<string> Gen(MethodDescription methodDescription, IEnumerable<string> properties, string entityName)
        {
            var resultMethods = new List<string>();
            methods = new List<string> { "ReadBy", "FindBy", "GetBy", "ReadListBy", "FindListBy", "GetListBy", "DeleteBy" };
            var baseMethodNames = new List<string>() { "Read", "Find", "Get" };
            var entityPlurName = new Pluralizer().Pluralize(entityName);
            methodNamePrefixs = baseMethodNames.Select(x => $"{x}{entityPlurName}By");
            propertyMethods = methods.SelectMany(x => properties.Select(y => $"{x}{y}")).ToList();
            allMethodPrefixs = new List<string>();
            allMethodPrefixs.AddRange(methods);
            allMethodPrefixs.AddRange(propertyMethods);
            allMethodPrefixs.AddRange(methodNamePrefixs);
            allMethodPrefixs.AddRange(methodNamePrefixs.SelectMany(x => properties.Select(y => $"{x}{y}")).ToList());
            if (methodDescription == null || string.IsNullOrWhiteSpace(methodDescription.Prefix))
            {
                resultMethods.AddRange(allMethodPrefixs);
                return resultMethods;
            }

            if (!methods.Union(methodNamePrefixs).Contains(methodDescription.Prefix, StringComparer.CurrentCultureIgnoreCase))
            {
                resultMethods.AddRange(allMethodPrefixs);
                return resultMethods;
            }

            var currentToken = methodDescription.Tokens.FirstOrDefault(x => x.IsCurrent);
            var currentTokenIndex = methodDescription.Tokens.IndexOf(currentToken);
            Token prevOneToken = null, prevToken2 = null, prevToken3 = null;
            if (currentTokenIndex > 0)
            {
                prevOneToken = methodDescription.Tokens[currentTokenIndex - 1];
            }

            if (currentTokenIndex > 1)
            {
                prevToken2 = methodDescription.Tokens[currentTokenIndex - 2];
            }

            if (currentTokenIndex > 3)
            {
                prevToken3 = methodDescription.Tokens[currentTokenIndex - 3];
            }

            var orderByTokenIndex = methodDescription.Tokens.FirstOrDefault(x => x.Type == State.OrderBy)?.BeginIndex ?? 99999;
            Token nextToken = currentTokenIndex < methodDescription.Tokens.Count - 1 ? methodDescription.Tokens[currentTokenIndex + 1] : null;
            switch (currentToken.Type)
            {
                case State.Prefix:
                    resultMethods.AddRange(allMethodPrefixs);
                    break;
                case State.PropertyName:
                    resultMethods.AddRange(properties);
                    break;
                case State.CalcOperator:
                    if (prevOneToken != null && prevOneToken.Type == State.PropertyName)
                    {
                        resultMethods.AddRange(MethodParser.OPERATORS);
                        break;
                    }
                    resultMethods.AddRange(MethodParser.LOGICOPERATORS);
                    break;
                case State.Operator:
                    if (currentToken.BeginIndex > orderByTokenIndex)
                    {
                        resultMethods.AddRange(MethodParser.SORTDIRECTIONOPERATORS.Union(new string[] { "And" }));
                        break;
                    }
                    resultMethods.AddRange(MethodParser.OPERATORS);
                    break;
                case State.LogicOperator:
                    if (prevOneToken != null && prevOneToken.Type == State.PropertyName)
                    {
                        resultMethods.AddRange(MethodParser.OPERATORS);
                        break;
                    }

                    if ((prevToken2 != null && prevToken2.Type == State.OrderBy)
                        || (prevToken3 != null && prevToken3.Type == State.OrderBy))
                    {
                        resultMethods.Add("And");
                        break;
                    }
                    resultMethods.AddRange(MethodParser.LOGICOPERATORS);
                    break;
                case State.OrderByDirection:
                    resultMethods.AddRange(MethodParser.SORTDIRECTIONOPERATORS);
                    if (nextToken == null || nextToken.Type != State.LogicOperator)
                    {
                        resultMethods.Add("And");
                        break;
                    }
                    break;
                default:
                    throw new NotSupportedException();
            }
            //var propertyLogic = methodDescription.Properties.LastOrDefault();
            //if (propertyLogic == null)
            //{
            //    resultMethods = properties.ToList();
            //}
            //else
            //{
            //    var propertyIndex = methodDescription.Properties.IndexOf(propertyLogic);
            //    if (propertyIndex >= 1 &&
            //        string.IsNullOrWhiteSpace(methodDescription.Properties[propertyIndex - 1].LogicOperator))
            //    {
            //        resultMethods = MethodParser.LOGICOPERATORS.ToList();
            //        //context.CompletionListSpan = new TextSpan(context.Position - propertyLogic.Name.Length, propertyLogic.Name.Length);
            //    }
            //    else if (!properties.Contains(propertyLogic.Name))
            //    {
            //        resultMethods = properties.ToList();
            //    }
            //    else
            //    {

            //        var possibleProperties = properties.Where(x =>
            //            x.Contains(propertyLogic.Name) && x != propertyLogic.Name).ToList();
            //        resultMethods.AddRange(MethodParser.OPERATORS);
            //        if (!string.IsNullOrWhiteSpace(propertyLogic.BinaryOperator))
            //        {
            //            resultMethods = resultMethods
            //                .Where(x => x.StartsWith(propertyLogic.BinaryOperator))
            //                .Where(x => x != propertyLogic.BinaryOperator)
            //                .ToList();
            //            if (!resultMethods.Any())
            //            {
            //                resultMethods.AddRange(MethodParser.LOGICOPERATORS);
            //            }
            //        }
            //        else
            //        {
            //            resultMethods.AddRange(possibleProperties);
            //            possibleProperties.Clear();
            //        }

            //        if (!string.IsNullOrWhiteSpace(propertyLogic.LogicOperator))
            //        {
            //            if (lastToken.Type == State.PropertyName)
            //            {
            //                var matchLogicOperators = resultMethods
            //                    .Where(x => x.StartsWith(propertyLogic.LogicOperator)).ToList();
            //                if (matchLogicOperators.Any(x => x == propertyLogic.LogicOperator))
            //                {
            //                    resultMethods = properties.ToList();
            //                    resultMethods.AddRange(matchLogicOperators.Except(
            //                        matchLogicOperators.Where(x => x == propertyLogic.LogicOperator)));
            //                }
            //            }
            //        }
            //        else if (possibleProperties.Any())
            //        {
            //            resultMethods.AddRange(possibleProperties);
            //        }
            //    }
            //}

            return resultMethods;
        }
    }
}
